﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Loader;
using System.Text;

using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyModel;

namespace Jack.RedisEventBus
{
    public static class RedisEventBusServiceCollectionExtentions
    {
        public static IServiceCollection AddRedisEventBus(this IServiceCollection services, Action<EventBusOption> configOptions, bool startSubscriper = true)
        {
            if (configOptions is null)
            {
                throw new ArgumentNullException(nameof(configOptions));
            }
            services.Configure(configOptions);
            services.AddSingleton<IEventPublisher, DefaultEventPublisher>();

            if (startSubscriper)
            {
                services.AddSingleton<IEventStore, DefaultEventStore>();
                RegisterEventHandlers(services);
                services.AddHostedService<SubscriperHostedService>();
            }

            return services;
        }

        private static void RegisterEventHandlers(IServiceCollection services)
        {
            var libs = DependencyContext.Default.CompileLibraries.Where(lib => lib.Serviceable == false && lib.Type == "project").Select(lib => lib.Name).ToList();
            var assemblies = libs.Select(lib => AssemblyLoadContext.Default.LoadFromAssemblyName(new System.Reflection.AssemblyName(lib))).ToArray();

            foreach (var assemblie in assemblies)
            {
                foreach (var eventHandlerType in assemblie.GetExportedTypes().Where(x => IsAssignableTo<IEventHandle>(x) && x.IsGenericType == false && x.IsAbstract == false && x.IsClass))
                {
                    services.AddSingleton(eventHandlerType);
                }
            }
        }

        private static bool IsAssignableTo<T>(Type @this)
        {
            if (@this == null)
            {
                throw new ArgumentNullException("this");
            }

            return typeof(T).IsAssignableFrom(@this);
        }
    }
}
